package edu.cmu.atlub.controllers;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.ArrayList;

import org.apache.http.client.ClientProtocolException;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import edu.cmu.atlub.api.AuthenticationManager;
import edu.cmu.atlub.api.FilterAPI;
import edu.cmu.atlub.api.FollowersAPI;
import edu.cmu.atlub.api.FollowingAPI;
import edu.cmu.atlub.api.Locations;
import edu.cmu.atlub.model.FollowsModel;
import edu.cmu.atlub.model.MutualFollowModel;
import edu.cmu.atlub.model.ProcessedSusModel;
import edu.cmu.atlub.model.SusModel;
import edu.cmu.atlub.persistence.ConnectionManager;

public class MainController {

    public static long[] getFollowersUserId(long targetUserId) throws ClientProtocolException, IOException {
	FollowersAPI followersAPI = new FollowersAPI();
	InputStream apiStream = followersAPI.getAPIInputStream(targetUserId);
	BufferedReader apiReader = new BufferedReader(new InputStreamReader(apiStream));

	String line = "";
	ArrayList<Long> followersList = new ArrayList<Long>();

	while ((line = apiReader.readLine()) != null) {
	    try {
		JSONArray jsonArray = new JSONArray(line);
		for (int i = 0; i < jsonArray.length(); i++) {
		    JSONObject oneFollower = jsonArray.getJSONObject(i);
		    long followerId = -1;
		    if (!oneFollower.isNull("id")) {
			followerId = oneFollower.getLong("id");
			followersList.add(followerId);
		    }
		}
	    } catch (JSONException e) {
		System.err.println(line);
		System.err.println(e.getMessage());
		return null;
	    }
	}

	long[] followers = new long[followersList.size()];
	for (int i = 0; i < followersList.size(); i++) {
	    followers[i] = followersList.get(i);
	}

	return followers;
    }

    public static long[] getFollowingUserId(long targetUserId) throws ClientProtocolException, IOException {
	FollowingAPI followingAPI = new FollowingAPI();
	InputStream apiStream = followingAPI.getAPIInputStream(targetUserId);
	BufferedReader apiReader = new BufferedReader(new InputStreamReader(apiStream));

	String line = "";
	ArrayList<Long> followingList = new ArrayList<Long>();

	while ((line = apiReader.readLine()) != null) {
	    try {
		JSONArray jsonArray = new JSONArray(line);
		for (int i = 0; i < jsonArray.length(); i++) {
		    JSONObject oneUser = jsonArray.getJSONObject(i);
		    long followeingId = -1;
		    if (!oneUser.isNull("id")) {
			followeingId = oneUser.getLong("id");
			followingList.add(followeingId);
		    }
		}
	    } catch (JSONException e) {
		System.err.println(line);
		System.err.println(e.getMessage());
		return null;
	    }
	}

	long[] followings = new long[followingList.size()];
	for (int i = 0; i < followingList.size(); i++) {
	    followings[i] = followingList.get(i);
	}

	return followings;
    }

    public static void insertIntoFollows() throws InstantiationException, IllegalAccessException,
	    ClassNotFoundException, SQLException, ClientProtocolException, IOException, JSONException {
	Connection dbConn = ConnectionManager.getConnection();
	long[] allUserIds = SusModel.getAllUserIds(dbConn, false);

	for (long targetUserId : allUserIds) {
	    long[] followerUserIds = getFollowersUserId(targetUserId);
	    long[] followingUserIds = getFollowingUserId(targetUserId);
	    
	    if (followerUserIds == null || followingUserIds == null) {
		System.err.printf("Stopping at user %d (not inserted) ... \n", targetUserId);
		dbConn.close();
		return;
	    }

	    for (long follower : followerUserIds) {
		System.err.printf("Inserting %d following %d ... \n", follower, targetUserId);
		FollowsModel.insertIntoFollows(follower, targetUserId, dbConn);
	    }

	    for (long following : followingUserIds) {
		System.err.printf("Inserting %d following %d ... \n", targetUserId, following);
		FollowsModel.insertIntoFollows(targetUserId, following, dbConn);
	    }
	    
	    ProcessedSusModel.insertUser(targetUserId, dbConn);
	}
	dbConn.close();
    }

    public static void insertIntoMutualFollow() throws InstantiationException, IllegalAccessException,
	    ClassNotFoundException, SQLException, ClientProtocolException, IOException, JSONException,
	    InterruptedException {

	Connection dbConn = ConnectionManager.getConnection();

	long[] allUserIds = SusModel.getAllUserIds(dbConn, false);
	for (long targetUserId : allUserIds) {
	    long[] followerUserIds = getFollowersUserId(targetUserId);
	    Thread.sleep(100);

	    for (long oneFollowerId : followerUserIds) {
		long[] followerFollowerIds = getFollowersUserId(oneFollowerId);
		for (int i = 0; i < followerFollowerIds.length; i++) {
		    long currFFId = followerFollowerIds[i];
		    if (currFFId == targetUserId) {
			System.err.printf("Found mutual follower: %d <=> %d, inserting ... \n", targetUserId,
				oneFollowerId);
			MutualFollowModel.insertMutualFollow(targetUserId, oneFollowerId, dbConn);
		    }
		}
	    }
	}
	dbConn.close();
    }

    public static void insertIntoSus(String logFile) throws ClientProtocolException, IOException, JSONException,
	    InterruptedException, SQLException, InstantiationException, IllegalAccessException, ClassNotFoundException {
	FilterAPI filterAPI = new FilterAPI();

	InputStream apiStream = filterAPI.getAPIInputStream(Locations.PITTSBURGH);
	BufferedReader apiReader = new BufferedReader(new InputStreamReader(apiStream));
	String line = "";
	BufferedWriter logWriter = new BufferedWriter(new FileWriter(logFile));

	Connection dbConn = ConnectionManager.getConnection();
	int numCrawled = 0;
	int numContainsCoordinates = 0;
	int maxNum = 2000;

	while ((line = apiReader.readLine()) != null && numCrawled <= maxNum) {
	    JSONObject jsonObj = new JSONObject(line);

	    // if (!jsonObj.isNull("coordinates") && !jsonObj.isNull("user")) {
	    if (!jsonObj.isNull("user")) {

		String strCoordinates = "[]";
		if (!jsonObj.isNull("coordinates")) {
		    numContainsCoordinates++;
		    JSONObject coordinates = (JSONObject) jsonObj.get("coordinates");
		    strCoordinates = coordinates.get("coordinates").toString();
		}

		JSONObject user = (JSONObject) jsonObj.get("user");
		long userId = Long.parseLong(user.get("id").toString());

		String logStr = String.format("%d: Inserting user %d at %s ... \n", numCrawled, userId, strCoordinates);

		System.err.printf(logStr);
		logWriter.write(logStr);
		logWriter.flush();

		SusModel.insertUser(userId, dbConn);

		numCrawled++;
	    }
	    Thread.sleep(100);
	}

	System.err.printf("Total num: %d,  contains coordinates: %d\n", numCrawled, numContainsCoordinates);
	logWriter
		.write(String.format("Total num: %d,  contains coordinates: %d\n", numCrawled, numContainsCoordinates));

	logWriter.close();
	dbConn.close();
    }

    /**
     * @param args
     * @throws JSONException
     * @throws IOException
     * @throws ClientProtocolException
     * @throws InterruptedException
     * @throws ClassNotFoundException
     * @throws IllegalAccessException
     * @throws InstantiationException
     * @throws SQLException
     */
    public static void main(String[] args) throws ClientProtocolException, IOException, JSONException,
	    InterruptedException, SQLException, InstantiationException, IllegalAccessException, ClassNotFoundException {
	// MainController.insertIntoSus("./insertIntoSus.log");
	// MainController.insertIntoMutualFollow();
	MainController.insertIntoFollows();
    }

}
